<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Logic;

use App\Model\ApplyExpert;
use App\Model\FollowExpert;
use App\Model\Game;
use App\Model\Play;
use App\Model\Project;
use App\Model\ProjectOrder;
use App\Model\UserModelExpert;
use App\Service\SettingService;
use App\Utils\Enum\AccountEnum;
use App\Utils\Enum\LiveEnum;
use App\Utils\Enum\MatchEnum;
use App\Utils\Enum\RedbillEnum;
use Exception;
use Hyperf\Context\ApplicationContext;
use Hyperf\DbConnection\Db;
use Hyperf\Redis\Redis;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use RedisException;

class RedbillLogic
{
    /**
     * ODDS依照国家.
     */
    public const string ODDS_DEFAULT_COMPANY_NAME = '英国';


    /**
     * 获取专家详情
     * @throws Exception
     */
    public static function getExpertDetail($userid, $expertUserId): array
    {
        $res = UserModelExpert::with(['user'])->where('userid', $expertUserId)->first();
        if (!$res) {
            throw new Exception('专家信息不存在');
        }
        $followUserIds = [];
        if ($userid) {
            if (FollowExpert::query()->where(['userid' => $userid, 'expert_userid' => $expertUserId])->exists()) {
                $followUserIds[] = $expertUserId;
            }
        }
        return self::formatExpertInfo($res->toArray(), $followUserIds, true);
    }

    /**
     * 获取过滤可选的赛事清单
     * @param $sportId
     * @return array
     */
    public static function getFilterGames($sportId): array
    {
        $gameIds = Project::query()->where(['sport_id' => $sportId, 'status' => RedbillEnum::STATUS_SALE_ING])->select('game_id')->distinct()->pluck('game_id')->toArray();
        if (empty($gameIds)) {
            return [];
        }
        return Game::query()->whereIn('game_id', $gameIds)->select(['game_id as gameId', 'name as gameName'])->get()->toArray();

    }

    public static function getProjectSports(): array
    {
        return [
            [
                'sportId' => 1,
                'sportName' => '足球'
            ],
            [
                'sportId' => 2,
                'sportName' => '篮球'
            ],
        ];
    }

    public static function getProjectPlayTypes(): array
    {
        return [
            MatchEnum::SPORT_FOOTBALL => [
                'sportId' => MatchEnum::SPORT_FOOTBALL,
                'sportName' => '足球',
                'playTypes' => RedbillEnum::getPlayList(MatchEnum::SPORT_FOOTBALL),
            ],
            MatchEnum::SPORT_BASKETBALL => [
                'sportId' => MatchEnum::SPORT_BASKETBALL,
                'playTypes' => RedbillEnum::getPlayList(MatchEnum::SPORT_BASKETBALL),
            ],
        ];
    }

    /**
     * 获取全部猜测项
     * @return array
     */
    public static function getSuggestItems(): array
    {
        $items = [];
        foreach ([RedbillEnum::PLAY_TYPE_FOOTBALL, RedbillEnum::PLAY_TYPE_BASKETBALL] as $types) {
            foreach ($types as $playTypeCode) {
                $items[$playTypeCode] = [
                    'playCode' => $playTypeCode,
                    'playName' => RedbillEnum::getPlayTypeName($playTypeCode),
                    'suggestItems' => RedbillEnum::getSuggestItemList($playTypeCode)
                ];
            }
        }
        return $items;
    }

    /**
     * 获取方案列表.
     * @param mixed $userid
     * @param mixed $input
     */
    public static function getProjectList($userid, $input): array
    {
        [$pageNum, $pageSize, $offset] = parsePageParams($input);
        $total = 0;
        $where = [];
        $followExpertUserIds = $gameIds = [];
        if (isset($input['query']['statusId']) && intval($input['query']['statusId']) == 2) {
            $where[] = ['status', '>', RedbillEnum::STATUS_SALE_ING];
        } else {
            $where[] = ['status', '=', RedbillEnum::STATUS_SALE_ING];
        }
        if (!empty($input['query']['matchId'])) {
            $where[] = ['match_id', '=', $input['query']['matchId']];
        }
        if (isset($input['query']['expertUserId']) && $input['query']['expertUserId']) {
            $where[] = ['userid', '=', (int)$input['query']['expertUserId']];
        }
        if (isset($input['query']['filterType']) && (int)$input['query']['filterType']) {
            if (intval($input['query']['filterType']) == 3) {
                if ($userid) {
                    $followExpertUserIds = FollowExpert::query()->where('userid', $userid)->select('expert_userid')->pluck('expert_userid')->toArray();
                    if (empty($followExpertUserIds)) {
                        goto RESULT;
                    }
                    unset($input['query']['playTypeCode'], $input['query']['gameIds']);
                } else {
                    goto RESULT;
                }
            } else {
                $where[] = ['sport_id', '=', (int)$input['query']['filterType']];
            }
        }
        if (isset($input['query']['playTypeCode']) && $input['query']['playTypeCode']) {
            $where[] = ['play_type_code', '=', (int)$input['query']['playTypeCode']];
        }
        if (!empty($input['query']['gameIds']) && is_array($input['query']['gameIds'])) {
            $gameIds = array_filter(array_unique($input['query']['gameIds']));
        }
        $total = Project::query()->where($where)->when(!empty($followExpertUserIds), function ($query) use ($followExpertUserIds) {
            return $query->whereIn('userid', $followExpertUserIds);
        })->when(!empty($gameIds), function ($query) use ($gameIds) {
            return $query->whereIn('game_id', $gameIds);
        })->count();
        $records = [];
        if ($total) {
            $order = isset($input['query']['orderType']) && $input['query']['orderType'] == 1 ? 'expert_win_rate' : 'expert_payback_rate';
            $rs = Project::with(['user', 'expert', 'match'])->where($where)->when(!empty($followExpertUserIds), function ($query) use ($followExpertUserIds) {
                return $query->whereIn('userid', $followExpertUserIds);
            })->when(!empty($gameIds), function ($query) use ($gameIds) {
                return $query->whereIn('game_id', $gameIds);
            })->offset($offset)->limit($pageSize)->orderBy($order, 'desc')->get();
            $projectIds = array_column($rs->toArray(), 'project_id');
            $paidProjectIds = [];
            if ($userid) {
                $orders = ProjectOrder::query()->where([['userid', '=', $userid], ['project_id', 'in', $projectIds]])->select(['project_id'])->get();
                if ($orders) {
                    $paidProjectIds = array_column($orders->toArray(), 'project_id');
                }
            }
            foreach ($rs->toArray() as $r) {
                $records[] = self::formatProject($r, $paidProjectIds);
            }
        }

        RESULT:
        return [
            'total' => $total,
            'pages' => ceil($total / $pageSize),
            'records' => $records ?? [],
        ];
    }

    /**
     * 获取方案详情.
     * @param mixed $userid
     * @param mixed $projectId
     * @throws Exception
     */
    public static function getProjectDetail($userid, $projectId): array
    {
        $project = Project::with(['user', 'expert', 'match'])->where('project_id', $projectId)->first()->toArray();
        if (!$project) {
            throw new Exception('爆料不存在');
        }
        $paidProjectIds = [];
        if (ProjectOrder::query()->where(['userid' => $userid, 'project_id' => $projectId])->exists()) {
            $paidProjectIds[] = $projectId;
        }
        $followedExpertUserIds = [];
        if ($userid && FollowExpert::query()->where(['userid' => $userid, 'expert_userid' => $project['userid']])->exists()) {
            $followedExpertUserIds = [$project['userid']];
        }
        $projectInfo = self::formatProject($project, $paidProjectIds, $followedExpertUserIds);

        $where = [
            'userid' => $project['userid'],
            'status' => $projectInfo['status'] == RedbillEnum::STATUS_SALE_ING ? RedbillEnum::STATUS_RESULT_PUBLISH : RedbillEnum::STATUS_SALE_ING,
        ];
        $projectInfo['productList'] = [];

        $productList = Project::with(['match'])->where($where)->limit(3)->orderBy('id', 'desc')->get()->toArray();
        if (count($productList)) {
            $paidProjectIds = [];
            if ($userid) {
                $projectIds = array_column($productList, 'project_id');
                $paidProjects = ProjectOrder::query()->where('userid', $userid)->whereIn('project_id', $projectIds)->select(['project_id'])->get();
                if ($paidProjects) {
                    $paidProjectIds = array_column($paidProjects->toArray(), 'project_id');
                }
            }
            foreach ($productList as $pro) {
                $pro['user'] = $project['user'];
                $pro['expert'] = $project['expert'];
                $projectInfo['productList'][] = self::formatProject($pro, $paidProjectIds);
            }
            unset($productList);
        }
        return $projectInfo;
    }

    /**
     * 发布爆料.
     * @param mixed $userid
     * @param mixed $input
     * @throws Exception
     */
    public static function addProject($userid, $input): array
    {
        $expert = UserModelExpert::query()->where('userid', $userid)->first();
        if (!$expert) {
            throw new Exception('无权限发布爆料');
        }

        $maxCountPerDay = SettingService::get('redbill_max_per_day', 10);
        if (Project::query()->where('userid', $userid)->whereDay('add_time', date('Y-m-d'))->count() >= $maxCountPerDay) {
            throw new Exception('每日发布次数超过限制');
        }
        $match = Play::with(['data' => function ($q) {
            $q->select(['match_id', 'odds']);
        }])->where('match_id', $input['matchId'])->first();
        if (!$match) {
            throw new Exception('比赛不存在');
        }
        if ($match['match_status'] != MatchEnum::MATCH_STATUS_WAIT || $match['match_time'] < time()) {
            throw new Exception('该比赛不可发布爆料');
        }
        $odds = self::filterOdds($match['data']['odds']);
        if (empty($odds)) {
            throw new Exception('该比赛尚未提供赔率数据，不可发布爆料');
        }
        $playTypeCodes = RedbillEnum::getPlayList($match['sport_id']);
        if (!in_array($input['playTypeCode'], array_column($playTypeCodes, 'code'))) {
            throw new Exception('玩法选择错误');
        }
        $oddsAlias = RedbillEnum::getOddsAlias($input['playTypeCode']);
        if (empty($odds[$oddsAlias])) {
            throw new Exception('该玩法缺少赔率数据，不可发布');
        }
        $allSuggestItems = RedbillEnum::getSuggestItemList($input['playTypeCode']);
        if (!empty(array_diff($input['suggestResultCode'], array_column($allSuggestItems, 'code'))) || count($input['suggestResultCode']) == 2 && $input['playTypeCode'] != RedbillEnum::PLAY_TYPE_FOOTBALL_WDL) {
            throw new Exception('推荐选项异常');
        }
        $priceList = self::getDefaultPriceList();
        if (!isset($priceList[$input['priceId']])) {
            throw new Exception('售价设置错误');
        }

        $project = Project::create([
            'userid' => $userid,
            'project_id' => Project::createId(),
            'sport_id' => $match['sport_id'],
            'play_type_code' => $input['playTypeCode'],
            'play_type_name' => RedbillEnum::getPlayTypeName($input['playTypeCode']),
            'game_id' => $match['game_id'],
            'match_id' => $match['match_id'],
            'match_odds' => new_json_encode($odds[$oddsAlias]),
            'title' => $match['home_team_name'] . ' VS ' . $match['away_team_name'],
            'introduce' => $input['introduce'],
            'expert_win_rate' => $expert['recent_win_rate'],
            'expert_payback_rate' => $expert['recent_payback_rate'],
            'status' => RedbillEnum::STATUS_SALE_ING,
            'coins' => $priceList[$input['priceId']]['price'],
            'sales' => 0,
            'total_amount' => 0,
            'is_hit' => 0,
            'hits' => 0,
            'suggest_result_code' => new_json_encode($input['suggestResultCode']),
            'real_result_code' => '[]',
            'add_time' => time(),
        ]);
        if (!$project) {
            throw new Exception('发布失败');
        }
        return [
            'projectId' => $project['project_id'],
        ];
    }

    /**
     * 获取可发布方案的比赛列表.
     * @param mixed $sportId
     */
    public static function getReleaseRelationInfo($sportId = 0): array
    {
        $where = [];
        if ($sportId) {
            $where[] = ['sport_id', '=', $sportId];
        }
        $where[] = ['match_status', '=', MatchEnum::MATCH_STATUS_WAIT]; // 未开赛
        $res = Play::query()->where($where)->whereBetween('match_time', [time(), time() + 7 * 86400])->whereHas('data', function ($q) {
            $q->whereRaw('length(odds) > 100');
        })->with(['data' => function ($q) {
            $q->select(['match_id', 'odds']);
        }])->orderBy('match_time')->get()->toArray();
        $matchList = [];
        foreach ($res as $r) {
            $odds = self::filterOdds($r['data']['odds']);
            if (empty($odds['asia']) || empty($odds['bs']) || empty($odds['eu'])) {
                continue;
            }
            $matchList[] = [
                'matchId' => $r['match_id'],
                'sportId' => $r['sport_id'],
                'gameId' => $r['game_id'],
                'gameName' => $r['game_name'],
                'gameLogo' => img_url($r['game_logo'], 48),
                'homeTeamId' => $r['home_team_id'],
                'homeTeamName' => $r['home_team_name'],
                'homeTeamLogo' => img_url($r['home_team_logo'], 48),
                'awayTeamId' => $r['away_team_id'],
                'awayTeamName' => $r['away_team_name'],
                'awayTeamLogo' => img_url($r['away_team_logo'], 48),
                'matchTime' => date('Y-m-d H:i', $r['match_time']),
                'matchTimestamp' => $r['match_time'],
                'matchOdds' => $odds,
            ];
        }
        //玩法
        $playTypes = RedbillEnum::getPlayList($sportId);
        array_walk($playTypes, function (&$type) {
            $type['alias'] = RedbillEnum::getOddsAlias($type['code']);
            $type['items'] = RedbillEnum::getSuggestItemList($type['code']);
        });

        return [
            'matchList' => $matchList,
            'playTypes' => $playTypes,
            'priceList' => array_values(self::getDefaultPriceList()),
        ];
    }

    /**
     * 获取预设价格
     * @return array
     */
    public static function getDefaultPriceList(): array
    {
        $setting = SettingService::get('redbill_price_list', '');
        $price = array_filter(array_unique(explode(',', trim($setting))));
        $data = [];
        foreach ($price as $i => $val) {
            $id = $i + 1;
            $data[$id] = [
                'id' => $id,
                'price' => $val
            ];
        }
        return $data;
    }

    /**
     * 过滤处理ODDS
     * @param $odds
     * @param $companyId
     * @return array
     */
    public static function filterOdds($odds, $companyId = 2): array
    {
        if (!is_array($odds)) {
            $odds = new_json_decode($odds);
        }
        $data = [];
        foreach ($odds as $type => $rs) {
            if (!in_array($type, ['asia', 'bs', 'eu']) || empty($rs)) {
                continue;
            }
            foreach ($rs as $r) {
                if ($r['company']['id'] == $companyId && !empty($r['f']['odd'])) {
                    $data[$type] = array_slice($r['f']['odd'], 0, 3);
                }
            }
        }
        return $data;
    }

    /**
     * 购买爆料.
     * @param mixed $userid
     * @param mixed $projectId
     * @param mixed $couponId
     * @throws Exception
     */
    public static function buyProject($userid, $projectId, $couponId = ''): true
    {
        if (ProjectOrder::query()->where(['userid' => $userid, 'project_id' => $projectId])->exists()) {
            throw new Exception('不可重复购买');
        }
        $project = Project::with(['match'])->where('project_id', $projectId)->first();
        if (!$project) {
            throw new Exception('爆料不存在');
        }
        if ($project['userid'] == $userid) {
            throw new Exception('不能购买自己的爆料');
        }
        if ($project['status'] != RedbillEnum::STATUS_SALE_ING) {
            throw new Exception('当前状态的爆料不可购买');
        }
        $ret = UserLogic::decrCoins($userid, $project['coins'], AccountEnum::TYPE_BUY_PROJECT, '购买爆料:' . $projectId, empty($projectId) ? [] : [$couponId]);
        if (!$ret) {
            throw new Exception('购买失败');
        }
        $order = ProjectOrder::create([
            'userid' => $userid,
            'project_id' => $projectId,
            'match_id' => $project['match_id'],
            'payment' => AccountEnum::PAYMENT_BALANCE,
            'coupon_id' => $couponId,
            'amount' => $project['coins'],
            'add_time' => time(),
        ]);
        if (!$order) {
            throw new Exception('购买失败');
        }
        Project::query()->where('project_id', $projectId)->increment('sales', 1);
        return true;
    }

    /**
     * 获取我发布的爆料.
     * @param mixed $userid
     * @param mixed $input
     */
    public static function getMyReleaseProjectList($userid, $input): array
    {
        [$pageNum, $pageSize, $offset] = parsePageParams($input);
        $total = Project::query()->where('userid', $userid)->count();
        if ($total) {
            $res = Project::with(['user', 'expert', 'match'])->where('userid', $userid)->offset($offset)->limit($pageSize)->orderBy('id', 'desc')->get()->toArray();
            $records = [];
            foreach ($res as $r) {
                $records[] = self::formatProject($r);
            }
        }
        return [
            'total' => $total,
            'pages' => ceil($total / $pageSize),
            'records' => $records ?? [],
        ];
    }

    /**
     * 获取我购买的爆料.
     * @param mixed $userid
     * @param mixed $input
     */
    public static function getMyPaidProjectList($userid, $input): array
    {
        [$pageNum, $pageSize, $offset] = parsePageParams($input);
        $total = ProjectOrder::query()->where('userid', $userid)->count();
        if ($total) {
            $res = ProjectOrder::with(['project' => function ($q) {
                $q->with(['expert', 'user']);
            }, 'match'])->where('userid', $userid)->orderBy('id', 'desc')->offset($offset)->limit($pageSize)->get()->toArray();
            $records = [];
            foreach ($res as $r) {
                $r['project']['match'] = $r['match'];
                $records[] = self::formatProject($r['project'], [$r['project']['project_id']]);
            }
        }
        return [
            'total' => $total,
            'pages' => ceil($total / $pageSize),
            'records' => $records ?? [],
        ];
    }

    /**
     * 获取排行榜.
     * @param mixed $type
     * @param mixed $topNum
     * @param int|string $userid
     * @throws Exception
     */
    public static function getRankList($userid, $type, $topNum): array
    {
        $orderField = match ($type) {
            RedbillEnum::RANK_TYPE_FANS => 'fans_count',
            RedbillEnum::RANK_TYPE_RED => 'recent_red_num',
            RedbillEnum::RANK_TYPE_HIT => 'recent_win_rate',
            RedbillEnum::RANK_TYPE_BACK => 'recent_payback_rate',
            default => '',
        };
        if ($orderField == '') {
            throw new Exception('不支持该排行榜');
        }
        $res = UserModelExpert::with(['user'])->orderBy($orderField, 'desc')->limit($topNum)->get();
        $records = [];
        $followExpertUserIds = [];
        if ($res && $userid) {
            $expertUserIds = array_column($res->toArray(), 'userid');
            $rs = FollowExpert::query()->where([['userid', '=', $userid], ['expert_userid', 'in', $expertUserIds]])->select(['expert_userid'])->get();
            if ($rs) {
                $followExpertUserIds = array_column($rs->toArray(), 'expert_userid');
            }
            unset($rs);
        }
        foreach ($res as $i => $r) {
            $records[] = self::formatExpertInfo($r->toArray(), $followExpertUserIds);
        }
        return $records;
    }

    /**
     * 关注/取关专家.
     * @param mixed $userid
     * @param mixed $expertUserId
     * @throws Exception
     */
    public static function follow($userid, $expertUserId): int
    {
        $info = FollowExpert::query()->where(['userid' => $userid, 'expert_userid' => $expertUserId])->first();
        Db::beginTransaction();
        if ($info) {
            $ret1 = FollowExpert::query()->where('id', $info['id'])->delete();
            $ret2 = UserModelExpert::query()->where('userid', $expertUserId)->decrement('fans_count');
            $followed = 0;
        } else {
            $ret1 = FollowExpert::create([
                'userid' => $userid,
                'expert_userid' => $expertUserId,
                'add_time' => time(),
            ]);
            $ret2 = UserModelExpert::query()->where('userid', $expertUserId)->increment('fans_count');
            $followed = 1;
        }
        if ($ret1 && $ret2) {
            Db::commit();
            return $followed;
        } else {
            Db::rollBack();
            throw new Exception('操作失败');
        }
    }

    /**
     * 获取比赛玩法指标.
     * @param mixed $odds
     * @param mixed $playTypeCode
     */
    private static function getMatchPlayOdds($odds, $playTypeCode): array|false
    {
        if (empty($odds)) {
            return false;
        }
        if (!is_array($odds)) {
            $odds = new_json_decode($odds);
        }
        if (empty($odds['companies'])) {
            return false;
        }
        $companyId = 0;
        foreach ($odds['companies'] as $c) {
            if ($c['name'] == self::ODDS_DEFAULT_COMPANY_NAME) {
                $companyId = $c['id'];
            }
        }
        if (!$companyId) {
            return false;
        }
        $funcCompanyOdds = function ($odds, $type) use ($companyId) {
            if (empty($odds[$type])) {
                return false;
            }
            foreach ($odds as $o) {
                if ($o['company']['id'] == $companyId) {
                    return array_slice($o['f'], 0, 3);
                }
            }
            return false;
        };
        switch ($playTypeCode) {
            case RedbillEnum::PLAY_TYPE_FOOTBALL_WDL:
            case RedbillEnum::PLAY_TYPE_BASKETBALL_WL:
                return $funcCompanyOdds($odds, 'eu');
            case RedbillEnum::PLAY_TYPE_FOOTBALL_BWL:
            case RedbillEnum::PLAY_TYPE_BASKETBALL_SWL:
                return $funcCompanyOdds($odds, 'asia');
            case RedbillEnum::PLAY_TYPE_FOOTBALL_BS:
            case RedbillEnum::PLAY_TYPE_BASKETBALL_BS:
                return $funcCompanyOdds($odds, 'bs');
            default:
                return false;
        }
    }

    /**
     * 格式化专家信息.
     */
    private static function formatExpertInfo(array $info, array $followedUserIds = [], $full = false): array
    {
        $data = [
            'expertId' => $info['userid'],
            'nickName' => $info['user']['nickname'],
            'avatar' => img_url($info['user']['avatar'], 80),
            'desc' => $info['expert_desc'],
            'redNum' => (int)$info['recent_red_num'],
            'winRate' => round(floatval($info['recent_win_rate']), 2),
            'backRate' => round(floatval($info['recent_payback_rate']), 2),
            'fansCount' => $info['fans_count'],
            'followed' => in_array($info['userid'], $followedUserIds) ? 1 : 0,
            'hitCount' => intval($info['recent_win_rate'] / 10),
            'totalCount' => 10,
        ];
        if ($full) {
            $data['introduce'] = $info['introduce'];
            $data['recentDayWin'] = $info['day_win'];
            $data['recentDayWalk'] = $info['day_walk'];
            $data['recentDayLose'] = $info['day_lose'];
            $data['recentCountWin'] = $info['count_win'];
            $data['recentCountWalk'] = $info['count_walk'];
            $data['recentCountLose'] = $info['count_lose'];
            $data['rankFansCount'] = $info['rank_fans_count'];
            $data['rankRedCount'] = $info['rank_red_count'];
            $data['rankPaybackCount'] = $info['rank_payback_count'];
            $data['fansCountRank'] = $info['fans_count_rank'] > 999 ? '999+' : $info['fans_count_rank'];
            $data['redNumRank'] = $info['recent_red_num_rank'] > 999 ? '999+' : $info['recent_red_num_rank'];
            $data['winRateRank'] = $info['recent_win_rate_rank'] > 999 ? '999+' : $info['recent_win_rate_rank'];
            $data['payBackRank'] = $info['recent_payback_rate_rank'] > 999 ? '999+' : $info['recent_payback_rate_rank'];
        }
        return $data;
    }

    /**
     * 格式化爆料项目.
     * @param array $info
     * @param array $paidProjectIds
     * @param array $followExpertUserIds
     * @return array
     */
    private static function formatProject(array $info, array $paidProjectIds = [], $followExpertUserIds = []): array
    {
        $isOpen = in_array($info['project_id'], $paidProjectIds) || $info['status'] == RedbillEnum::STATUS_RESULT_PUBLISH;
        return [
            'projectId' => $info['project_id'],
            'sportId' => $info['match']['sport_id'],
            'gameId' => $info['match']['game_id'],
            'matchId' => $info['match']['match_id'],
            'matchDetail' => [
                'sportId' => $info['match']['sport_id'],
                'matchId' => $info['match']['match_id'],
                'gameName' => $info['match']['game_name'],
                'gameLogo' => img_url($info['match']['game_logo']),
                'homeTeamName' => $info['match']['home_team_name'],
                'homeTeamLogo' => img_url($info['match']['home_team_logo']),
                'homeScores' => new_json_decode($info['match']['home_team_scores']),
                'awayTeamName' => $info['match']['away_team_name'],
                'awayTeamLogo' => img_url($info['match']['away_team_logo']),
                'awayScores' => new_json_decode($info['match']['away_team_scores']),
                'matchTime' => $info['match']['match_time'],
            ],
            'expert' => self::formatExpertInfo(array_merge($info['expert'], ['user' => $info['user']]), $followExpertUserIds),
            'playTypeCode' => $info['play_type_code'],
            'playTypeName' => $info['play_type_name'],
            'title' => $info['title'],
            'price' => $info['coins'],
            'status' => $info['status'],
            'hitStatus' => $info['hit_status'],
            'hits' => $info['hits'],
            'sales' => $info['sales'],
            'releaseTime' => date('Y-m-d H:i:s', $info['add_time']),
            'releaseTimestamp' => $info['add_time'],
            'odds' => new_json_decode($info['match_odds']),
            'matchResultCode' => $isOpen ? self::calcMatchResultCode(new_json_decode($info['suggest_result_code'])) : -1,
            'introduce' => $isOpen ? $info['introduce'] : null,
            'ownStatus' => in_array($info['project_id'], $paidProjectIds) ? 1 : 0,
        ];
    }

    /**
     * 获取排行
     * @param $expertUserId
     * @param string $type
     * @return array|false|int|mixed
     */
    public static function getRankIndex($expertUserId, string $type = ''): mixed
    {
        try {
            $redis = ApplicationContext::getContainer()->get(Redis::class);
            if ($type == '') {
                return [
                    'fansCountRank' => self::_getExpertRankIndex($redis, $expertUserId, 'fans'),
                    'redNumRank' => self::_getExpertRankIndex($redis, $expertUserId, 'redNum'),
                    'winRateRank' => self::_getExpertRankIndex($redis, $expertUserId, 'winRate'),
                    'payBackRank' => self::_getExpertRankIndex($redis, $expertUserId, 'payBack'),
                ];
            } else {
                return self::_getExpertRankIndex($redis, $expertUserId, $type);
            }
        } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
            return false;
        }
    }

    /**
     * 获取排行
     * @param $redis
     * @param $expertUserId
     * @param $type
     * @return int
     */
    private static function _getExpertRankIndex($redis, $expertUserId, $type): int
    {
        $prefix = 'expert:rank:';
        try {
            $rank = $redis->zRevRank($prefix . $type, $expertUserId);
            $rank = $rank === false ? 0 : $rank + 1;
        } catch (RedisException $e) {
            $rank = 0;
        }
        return $rank;
    }

    /**
     * 设置排行数据
     * @param $expertUserId
     * @param $type
     * @param $value
     * @return bool
     */
    public static function setExpertRankData($expertUserId, $type, $value): bool
    {
        try {
            $redis = ApplicationContext::getContainer()->get(Redis::class);
            $redis->zAdd('expert:rank:' . $type, $value, $expertUserId);
            return true;
        } catch (NotFoundExceptionInterface|ContainerExceptionInterface|RedisException $e) {
            return false;
        }
    }


    /**
     * 计算matchResultCode 用于推荐标志
     * @param $suggestResultCode
     * @return int
     */
    public static function calcMatchResultCode($suggestResultCode): int
    {
        $matchResultCode = 0;
        if (count($suggestResultCode) == 1) {
            switch ($suggestResultCode[0]) {
                //胜
                case RedbillEnum::SUGGEST_FB_WDL_HOME_WIN:
                case RedbillEnum::SUGGEST_FB_BWL_HOME_WIN:
                case RedbillEnum::SUGGEST_FB_BS_BIG:
                case RedbillEnum::SUGGEST_BB_WL_HOME_WIN:
                case RedbillEnum::SUGGEST_BB_SWL_HOME_WIN:
                case RedbillEnum::SUGGEST_BB_BS_BIG:
                    $matchResultCode = 1;
                //平
                case RedbillEnum::SUGGEST_FB_WDL_DRAW:
                    $matchResultCode = 2;
                    break;
                //负
                case RedbillEnum::SUGGEST_FB_WDL_AWAY_WIN:
                case RedbillEnum::SUGGEST_FB_BWL_AWAY_WIN:
                case RedbillEnum::SUGGEST_FB_BS_SMALL:
                case RedbillEnum::SUGGEST_BB_WL_AWAY_WIN:
                case RedbillEnum::SUGGEST_BB_SWL_AWAY_WIN:
                case RedbillEnum::SUGGEST_BB_BS_SMALL:
                    $matchResultCode = 3;
                    break;
            }
        } else {
            if (empty(array_diff($suggestResultCode, [RedbillEnum::SUGGEST_FB_WDL_HOME_WIN, RedbillEnum::SUGGEST_FB_WDL_DRAW]))) {
                $matchResultCode = 4;
            } elseif (empty(array_diff($suggestResultCode, [RedbillEnum::SUGGEST_FB_WDL_DRAW, RedbillEnum::SUGGEST_FB_WDL_AWAY_WIN]))) {
                $matchResultCode = 5;
            }
        }
        return $matchResultCode;
    }

    /**
     * 获取最后一次入驻记录
     * @param $userid
     * @return array
     */
    public static function getLastSettledResult($userid): array
    {
        $info = ApplyExpert::query()->where('userid', $userid)->orderBy('id', 'desc')->first();
        if (!$info) {
            return [];
        } else {
            return [
                'applyTime' => date('Y-m-d H:i', $info['apply_time']),
                'auditTime' => $info['audit_time'] ? date('Y-m-d H:i', $info['audit_time']) : 0,
                'auditStatus' => $info['audit_status'],
                'auditMark' => $info['audit_mark']
            ];
        }
    }

    /**
     * 入驻申请
     * @throws Exception
     */
    public static function settled($userid, $input): array
    {
        $lastRecord = self::getLastSettledResult($userid);
        if (!empty($lastRecord)) {
            if ($lastRecord['auditStatus'] == 0) {
                throw new Exception('不可重复提交申请');
            } else if ($lastRecord['auditStatus'] == 1) {
                throw new Exception('已入驻成功，无需再次提交申请');
            }
        }

        try {
            $ret = ApplyExpert::create([
                'userid' => $userid,
                'contact' => $input['contact'],
                'telephone' => $input['telephone'],
                'introduce' => $input['introduce'],
                'apply_time' => time(),
            ]);
            if ($ret) {
                return self::getLastSettledResult($userid);
            } else {
                throw new Exception('申请提交失败');
            }
        } catch (Exception $e) {
            throw new Exception('申请提交异常');
        }
    }
}
